/*
 * $Id: GameMapIO.java 23 2009-03-31 23:37:36Z aaron.day $
 */
package org.crazydays.gameplan.data;


import java.awt.image.BufferedImage;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.imageio.ImageIO;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.crazydays.gameplan.map.Geometry;
import org.crazydays.gameplan.map.IGameMap;
import org.crazydays.gameplan.map.location.Location;

/**
 * GameMapIO
 */
public class GameMapIO
{
	/** log */
	protected final static Log LOG = LogFactory.getLog(GameMapIO.class);

	/** map directory */
	public final static String MAP_DIRECTORY = "gamemaps";

	/** map image type */
	public final static String MAP_IMAGE_TYPE = "png";

	/** map image extension */
	public final static String MAP_IMAGE_EXTENSION = "png";

	/** map geometry extension */
	public final static String MAP_GEOMETRY_EXTENSION = "geo";

	/** map locations extension */
	public final static String MAP_LOCATIONS_EXTENSION = "loc";

	/**
	 * Save game map to zip output stream.
	 * 
	 * @param gameMap GameMap
	 * @param zipOutputStream ZipOutputStream
	 * @throws IOException
	 */
	public static void save(IGameMap gameMap,
	        ZipOutputStream zipOutputStream)
	        throws IOException
	{
		if (gameMap == null) {
			throw new IllegalArgumentException("gameMap == null");
		}
		if (zipOutputStream == null) {
			throw new IllegalArgumentException(
			        "zipOutputStream == null");
		}

		// image
		storeImage(gameMap.getName(), gameMap.getImage(),
		        zipOutputStream);

		// geometry
		storeGeometry(gameMap.getName(), gameMap.getGeometry(),
		        zipOutputStream);

		// locations
		storeLocations(gameMap.getName(), gameMap.getLocations(),
		        zipOutputStream);
	}

	/**
	 * Store image.
	 * 
	 * @param name Name
	 * @param image Image
	 * @param zipOutputStream ZipOutputStream
	 * @throws IOException
	 */
	public static void storeImage(String name, BufferedImage image,
	        ZipOutputStream zipOutputStream)
	        throws IOException
	{
		if (name == null) {
			throw new IllegalArgumentException("name == null");
		}
		if (image == null) {
			throw new IllegalArgumentException("image == null");
		}
		if (zipOutputStream == null) {
			throw new IllegalArgumentException(
			        "zipOutputStream == null");
		}

		// entry
		ZipEntry entry =
		        new ZipEntry(MAP_DIRECTORY + File.separator + name
		                + "." + MAP_IMAGE_EXTENSION);

		// write
		zipOutputStream.putNextEntry(entry);
		ImageIO.write(image, MAP_IMAGE_TYPE, zipOutputStream);
		zipOutputStream.closeEntry();
	}

	/**
	 * Store geometry.
	 * 
	 * @param name Name
	 * @param geometry Geometry
	 * @param zipOutputStream ZipOutputStream
	 * @throws IOException
	 */
	public static void storeGeometry(String name, Geometry geometry,
	        ZipOutputStream zipOutputStream)
	        throws IOException
	{
		if (name == null) {
			throw new IllegalArgumentException("name == null");
		}
		if (geometry == null) {
			throw new IllegalArgumentException("geometry == null");
		}
		if (zipOutputStream == null) {
			throw new IllegalArgumentException(
			        "zipOutputStream == null");
		}

		// geometry
		ZipEntry entry =
		        new ZipEntry(MAP_DIRECTORY + File.separator + name
		                + "." + MAP_GEOMETRY_EXTENSION);
		zipOutputStream.putNextEntry(entry);

		XMLEncoder geometryEncoder =
		        new XMLEncoder(new XMLEncoderOutputStreamWrapper(
		                zipOutputStream));
		geometryEncoder.writeObject(geometry);
		geometryEncoder.close();

		zipOutputStream.closeEntry();
	}

	/**
	 * Store locations.
	 * 
	 * @param name Name
	 * @param locations Locations
	 * @param zipOutputStream ZipOutputStream
	 * @throws IOException
	 */
	public static void storeLocations(String name, Set<Location> locations,
	        ZipOutputStream zipOutputStream)
	        throws IOException
	{
		if (name == null) {
			throw new IllegalArgumentException("name == null");
		}
		if (locations == null) {
			throw new IllegalArgumentException("locations == null");
		}
		if (zipOutputStream == null) {
			throw new IllegalArgumentException(
			        "zipOutputStream == null");
		}

		// geometry
		ZipEntry entry =
		        new ZipEntry(MAP_DIRECTORY + File.separator + name
		                + "." + MAP_LOCATIONS_EXTENSION);
		zipOutputStream.putNextEntry(entry);

		XMLEncoder geometryEncoder =
		        new XMLEncoder(new XMLEncoderOutputStreamWrapper(
		                zipOutputStream));
		geometryEncoder.writeObject(locations);
		geometryEncoder.close();

		zipOutputStream.closeEntry();
	}

	/**
	 * Load image from input stream.
	 * 
	 * @param inputStream InputStream
	 * @return BufferedImage
	 * @throws IOException
	 */
	public static BufferedImage loadImage(InputStream inputStream)
	        throws IOException
	{
		if (inputStream == null) {
			throw new IllegalArgumentException(
			        "inputStream == null");
		}

		// read image from input stream
		return ImageIO.read(inputStream);
	}

	/**
	 * Load geometry.
	 * 
	 * @param inputStream InputStream
	 * @return Geometry
	 */
	public static Geometry loadGeometry(InputStream inputStream)
	{
		if (inputStream == null) {
			throw new IllegalArgumentException(
			        "inputStream == null");
		}

		inputStream = new XMLDecoderInputStreamWrapper(inputStream);

		XMLDecoder decoder = new XMLDecoder(inputStream);
		try {
			return (Geometry) decoder.readObject();
		} finally {
			decoder.close();
		}
	}

	/**
	 * Load locations.
	 * 
	 * @param inputStream InputStream
	 * @return Locations
	 */
	public static Set<Location> loadLocations(InputStream inputStream)
	{
		if (inputStream == null) {
			throw new IllegalArgumentException(
			        "inputStream == null");
		}

		inputStream = new XMLDecoderInputStreamWrapper(inputStream);

		XMLDecoder decoder = new XMLDecoder(inputStream);
		try {
			@SuppressWarnings("unchecked")
			Set<Location> locations =
			        (Set<Location>) decoder.readObject();
			return locations;
		} finally {
			decoder.close();
		}
	}
}
